package concurrent;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 需求：一个jvm中实现一个计数器功能，需保证多线程情况下数据正确性。
 *
 * @author jack.wu
 * @since 2019/12/24 17:37
 */
public class CountDemo {

}

/**
 * 方式一：synchronized方式实现
 */
class Demo1 {
    static int count = 0;

    public static synchronized void incr() {
        count++;
    }

    private static void m1() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        int threadCount = 50;
        CountDownLatch downLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000; j++) {
                        incr();
                    }
                } finally {
                    downLatch.countDown();
                }

            }).start();
        }
        downLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("[耗时：%s，结果：%s]", (endTime - startTime), count));
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            count = 0;
            m1();
        }
    }

}

/**
 * 方式2：AtomicLong实现
 * AtomicLong内部采用CAS的方式实现，并发量大的情况下，
 * CAS失败率比较高，导致性能比synchronized还低一些。并发量不是太大的情况下，CAS性能还是可以的
 */
class Demo2 {
    static AtomicLong count = new AtomicLong(0);

    public static void incr() {
        count.incrementAndGet();
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            count.set(0);
            m1();
        }
    }

    private static void m1() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        int threadCount = 50;
        CountDownLatch downLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000; j++) {
                        incr();
                    }
                } finally {
                    downLatch.countDown();
                }
            }).start();
        }
        downLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("[耗时：%s，结果：%s]", (endTime - startTime), count.get()));
    }
}


/**
 * LongAdder是JDK1.8开始出现的，所提供的API基本上可以替换掉原先的AtomicLong。
 * LongAdder在并发量比较大的情况下，操作数据的时候，相当于把这个数字分成了很多份数字，然后交给多个人去管控，
 * 每个管控者负责保证部分数字在多线程情况下操作的正确性。
 * 当多线程访问的时，通过hash算法映射到具体管控者去操作数据，最后再汇总所有的管控者的数据，得到最终结果。
 * 相当于降低了并发情况下锁的粒度，所以效率比较高
 */
class Demo3 {
    static LongAdder count = new LongAdder();

    public static void incr() {
        count.increment();
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            count.reset();
            m1();
        }
    }

    private static void m1() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        int threadNum = 50;
        CountDownLatch downLatch = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000; j++) {
                        incr();
                    }
                } finally {
                    downLatch.countDown();
                }

            }).start();
        }
        downLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("[耗时：%s，结果：%s]", (endTime - startTime), count));
    }
}

/**
 * LongAccumulator是LongAdder的功能增强版。LongAdder的API只有对数值的加减，
 * 而LongAccumulator提供了自定义的函数操作
 */
class Demo4 {

    static LongAccumulator count = new LongAccumulator(Long::sum, 0L);

    public static void incr() {
        count.accumulate(1);
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            count.reset();
            m1();
        }
    }

    private static void m1() throws InterruptedException {
        long startTime = System.currentTimeMillis();
        int threadNum = 50;
        CountDownLatch downLatch = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            try {
                for (int j = 0; j < 1000; j++) {
                    incr();
                }
            } finally {
                downLatch.countDown();
            }
        }
        downLatch.await();
        long endTme = System.currentTimeMillis();
        System.out.println(String.format("[耗时：%s，结果：%s]", (endTme - startTime), count));
    }
}
